package com.risen.project.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.risen.project.common.*;
import com.risen.project.config.LocalDateTimeConverter;
import com.risen.project.constant.CommonConstant;
import com.risen.project.domain.ProjectManage;
import com.risen.project.domain.dto.ProjectManageDTO;
import com.risen.project.domain.vo.ProjectManageVO;
import com.risen.project.enums.*;
import com.risen.project.excel.handler.InvokeHandler;
import com.risen.project.excel.handler.ParamConvertHandler;
import com.risen.project.excel.listener.ReadConvertInvokeListener;
import com.risen.project.excel.listener.ReadHeadListener;
import com.risen.project.mapper.ProjectManageMapper;
import com.risen.project.service.ProjectManageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目管理信息接口实现类
 */
@Slf4j
@Service
public class ProjectManageServiceImpl extends ServiceImpl<ProjectManageMapper, ProjectManage> implements ProjectManageService {

    @Resource
    private ProjectManageMapper projectManageMapper;

    @Value("${config.transfixion.add}")
    private String transfixionAddUrl;

    @Override
    public Map<String, Integer> sendProjectManage(String baseProjIds) {

        Map<String, Integer> result = new HashMap<>();
        Integer maxId = 0;
        int success = 0;
        int fail = 0;
        while (true) {
            List<ProjectManage> projectManages;
            if (StrUtil.isEmpty(baseProjIds)) {
                projectManages = projectManageMapper.selectList(new QueryWrapper<ProjectManage>()
                        .in("sync_status", SyncStatusEnum.NOT_SYNC.getCode(), SyncStatusEnum.SYNC_FAIL.getCode())
                        .gt("id", maxId)
                        .last("limit 1"));

                if (CollectionUtils.isEmpty(projectManages)) {
                    log.info("查询不到数据");
                    break;
                }
            } else {
                QueryWrapper<ProjectManage> query = new QueryWrapper<>();
                query.in("base_proj_id", StrUtil.splitTrim(baseProjIds, ","));
                projectManages = projectManageMapper.selectList(query);
            }

            for (ProjectManage projectManage : projectManages) {
                Boolean send = doSend(projectManage);
                if (send) {
                    success++;
                } else {
                    fail++;
                }
            }

            // 项目id查询执行一遍直接跳出
            if (StrUtil.isNotEmpty(baseProjIds)) {
                break;
            }
            maxId = projectManages.get(projectManages.size() - 1).getId();
        }
        result.put("success", success);
        result.put("fail", fail);

        return result;
    }

    @Override
    public Integer insertProjectManage(ProjectManageDTO data) {

        ProjectManage projectManage = new ProjectManage();
        BeanUtils.copyProperties(data, projectManage);
        // 参数校验
//        validateParam(projectManage);
        // 是否存在
        projectManage.setSyncStatus(SyncStatusEnum.NOT_SYNC.getCode());
        return projectManageMapper.insert(projectManage);
    }

    @Override
    public Integer updateProjectManage(ProjectManageDTO data) {
        ProjectManage projectManage = new ProjectManage();
        BeanUtils.copyProperties(data, projectManage);
        // 参数校验
//        validateParam(projectManage);
        // 是否存在
        projectManage.setSyncStatus(SyncStatusEnum.NOT_SYNC.getCode());
        return projectManageMapper.update(projectManage, new QueryWrapper<ProjectManage>()
                .eq("sys_proj_id", projectManage.getSysProjId()));
    }

    @Override
    public String importProjectManageByExcel(MultipartFile file) {
        // 表格文件校验
        ExcelValidator.validateExcel(file.getOriginalFilename(), true);
        // 创建表格表头读取器 内部会自动校验表头 如果校验不通过的话 会将信息放 errorMsg 里面
        ReadHeadListener<ProjectManageDTO> readHeadListener = new ReadHeadListener<>(ProjectManageDTO.class);
        try {
            EasyExcel.read(file.getInputStream(), readHeadListener).sheet().doRead();
        } catch (IOException exception) {
            exception.printStackTrace();
            throw new ExceptionCore("文件读取失败");
        }
        String errorMsg = readHeadListener.getErrorMsg();
        if (!ObjectUtils.isEmpty(errorMsg)) {
            throw new ExceptionCore(errorMsg);
        }

        CommonThreadPool.getExecutorService().execute(() -> {
            // 参数转换器
            ParamConvertHandler<ProjectManageDTO, List<ProjectManage>> paramConvertHandler = data -> {
                List<ProjectManage> projectManages = new ArrayList<>(data.size());
                data.forEach(e -> {
                    ProjectManage projectManage = new ProjectManage();
                    BeanUtils.copyProperties(e, projectManage);
                    projectManage.setSyncStatus(SyncStatusEnum.NOT_SYNC.getCode());
                    projectManages.add(projectManage);
                });
                return projectManages;
            };

            // 创建执行器
            InvokeHandler<List<ProjectManage>, Boolean> invokeHandler = this::saveBatch;

            try {
                // 创建读取转换执行监听器
                ReadConvertInvokeListener<ProjectManageDTO, List<ProjectManage>, Boolean> listener = new ReadConvertInvokeListener<>(200, paramConvertHandler, invokeHandler);
                EasyExcel.read(file.getInputStream(), ProjectManageDTO.class, listener).sheet().doRead();
                if (!ObjectUtils.isEmpty(listener.getErrorMsg())) {
                    log.error(errorMsg);
                } else {
                    log.info("导入完成");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        return "开始导入";
    }

    @Override
    public void exportProjectManage(String baseProjName, String baseApplyDept, String createdAt,
                                    Integer reportStatus, HttpServletResponse response) {
        // 文件名称
        String fileName = SyncStatusEnum.getMsgByCode(reportStatus) + "项目信息导出" + System.currentTimeMillis() + ".xlsx";

        response.addHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));

        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(response.getOutputStream(), ProjectManageDTO.class)
                    .registerConverter(new LocalDateTimeConverter())
                    .build();

            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet(SyncStatusEnum.getMsgByCode(reportStatus)).head(ProjectManageDTO.class)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();

            // 分页参数
            int page = 1;
            // 每页批次大小
            int size = 10000;
            // 查询条件对象
            QueryWrapper<ProjectManage> query = buildQuery(baseProjName, baseApplyDept, createdAt, reportStatus);
            // 调用写入
            while (true) {
                IPage<ProjectManage> pageInfo = new Page<>(page, size);

                // 调用分页查询
                projectManageMapper.selectPage(pageInfo, query);
                List<ProjectManage> records = pageInfo.getRecords();

                // 收集导出对象集合
                List<ProjectManageDTO> dataList = records.stream().map(r -> {
                    ProjectManageDTO dto = new ProjectManageDTO();
                    BeanUtils.copyProperties(r, dto);

                    //枚举相关赋值
                    // 项目类型
                    dto.setBaseProjType(ProjectTypeEnum.getMsgByCode(dto.getBaseProjType()));
                    // 是否首次新建
                    dto.setBaseProjWhetNew(FirstCreateFlagEnum.getMsgByCode(dto.getBaseProjWhetNew()));
                    // 项目状态
                    dto.setBaseProjSetProg(ProjectStatusEnum.getMsgByCode(dto.getBaseProjSetProg()));
                    // 预算来源
                    dto.setBaseProjAmountOri(CapitalBudgetEnum.getMsgByCodeSplit(dto.getBaseProjAmountOri(), "；"));
                    // 立项依据
                    dto.setBaseProjBasis(ProjectBasisEnum.getMsgByCodeSplit(dto.getBaseProjBasis(), "；"));
                    // 建设层级
                    dto.setBaseProjConsClass(ConstructionHierarchyEnum.getMsgByCode(dto.getBaseProjConsClass()));
                    // 信息是否有效
                    dto.setIsEffective(CommBitFlagEnum.getMsgByCode(dto.getIsEffective()));
                    // 建设类型
                    dto.setBaseConstructionType(ConstructionTypeEnum.getMsgByCodeSplit(dto.getBaseConstructionType(), "；"));

                    return dto;
                }).collect(Collectors.toList());

                excelWriter.write(dataList, writeSheet);

                // 查询到最后一页/无数据
                if (page == pageInfo.getPages() || 0 == pageInfo.getPages()) {
                    break;
                }
                page++;
            }
        } catch (Exception e) {
            throw new ExceptionCore(fileName + "导出失败");
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    @Override
    public Response<List<ProjectManageVO>> getProjectList(String baseProjName, String baseApplyDept, String createdAt,
                                                          Integer reportStatus, int page, int size) {
        IPage<ProjectManage> pageInfo = new Page<>(page, size);

        // 调用分页查询
        projectManageMapper.selectPage(pageInfo, buildQuery(baseProjName, baseApplyDept, createdAt, reportStatus));

        List<ProjectManageVO> voList = pageInfo.getRecords().stream().map(c -> {
            ProjectManageVO vo = new ProjectManageVO();
            BeanUtil.copyProperties(c, vo);
            return vo;
        }).collect(Collectors.toList());

        return Response.success(voList, pageInfo.getTotal());
    }

    /**
     * 构建查询条件对象方法
     */
    private QueryWrapper<ProjectManage> buildQuery(String baseProjName, String baseApplyDept, String createdAt,
                                                   Integer reportStatus) {
        QueryWrapper<ProjectManage> query = new QueryWrapper<>();

        // 项目名称
        if (StrUtil.isNotEmpty(baseProjName)) {
            query.like("base_proj_name", baseProjName);
        }
        // 申请单位
        if (StrUtil.isNotEmpty(baseApplyDept)) {
            query.like("base_apply_dept", baseApplyDept);
        }
        // 申请时间
        if (StrUtil.isNotEmpty(createdAt)) {
            LocalDate createdAtForLocalDate = LocalDate.parse(createdAt, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            query.between("created_at", TimeUtil.dayStartTime(createdAtForLocalDate), TimeUtil.dayEndTime(createdAtForLocalDate));
        }
        // 上报状态
        if (ObjectUtil.isNotEmpty(reportStatus)) {
            query.eq("sync_status", reportStatus);
        }

        return query;
    }

    /**
     * 参数校验
     *
     * @param data 参数
     */
    private void validateParam(ProjectManage data) {
        // 项目状态
        String baseprojsetprog = data.getBaseProjSetProg();

        // 已立项-未启动
        if (ProjectStatusEnum.YLX_WQD.getCode().equals(baseprojsetprog)) {
            if (ObjectUtils.isEmpty(data.getApprovalFile())) {
                throw new ExceptionCore("立项批复文件必填");
            }
        }

        // 建设中的话 需要校验部分参数
        if (ProjectStatusEnum.YLX_JSZ.getCode().equals(baseprojsetprog)) {
            List<String> errorMsg = new LinkedList<>();
            if (ObjectUtils.isEmpty(data.getPurchaseFile())) {
                errorMsg.add("采购文件不能为空");
            }
            if (ObjectUtils.isEmpty(data.getBiddingFile())) {
                errorMsg.add("中标（成交）通知书不能为空");
            }
            if (ObjectUtils.isEmpty(data.getPurchaseContract())) {
                errorMsg.add("采购合同不能为空");
            }
            if (ObjectUtils.isEmpty(data.getBaseConsDeprt())) {
                errorMsg.add("承建单位不能为空");
            }
            if (ObjectUtils.isEmpty(data.getBaseConsDeprtUsci())) {
                errorMsg.add("承担单位统一信用代码不能为空");
            }
            if (ObjectUtils.isEmpty(data.getPurchaseFile())) {
                errorMsg.add("采购文件不能为空");
            }
            if (!CollectionUtils.isEmpty(errorMsg)) {
                throw new ExceptionCore(errorMsg.toString());
            }
        }

        // 已立项-已验收
        if (ProjectStatusEnum.YLX_YYS.getCode().equals(baseprojsetprog)) {
            if (ObjectUtils.isEmpty(data.getBaseAcceptFile())) {
                throw new ExceptionCore("验收报告不能为空");
            }
        }

        // 已立项-变更中
        if (ProjectStatusEnum.YLX_BGJSZ.getCode().equals(baseprojsetprog)) {
            if (ObjectUtils.isEmpty(data.getBaseChanFile())) {
                throw new ExceptionCore("变更批复文件不能为空");
            }
        }
    }

    /**
     * 数据上报
     *
     * @param data 上报的数据
     */
    private Boolean doSend(ProjectManage data) {
        String jsonBody = JSON.toJSONString(data);
        log.info("请求数据贯通接口 {} 参数 {}", transfixionAddUrl, jsonBody);

        String body = null;
        try {
            body = HttpRequest.post(transfixionAddUrl)
                    .timeout(10000)
                    .body(jsonBody)
                    .execute()
                    .body();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("请求数据贯通接口发生异常");
        }
        if (ObjectUtils.isEmpty(body)) {
            log.error("请求数据贯通接口返回值为空");
            return false;
        }
        log.info("请求数据贯通接口 {} 返回值 {}", transfixionAddUrl, body);
        JSONObject jsonObject = JSON.parseObject(body);
        // 返回状态code
        String code = null;
        // 返回结果data
        JSONObject resultData;
        if (jsonObject.containsKey("data")) {
            resultData = jsonObject.getJSONObject("data");
            code = resultData.getString("code");
        }

        data.setSyncStatus(CommonConstant.OK.equals(code) ? SyncStatusEnum.SYNC_SUCCESS.getCode() : SyncStatusEnum.SYNC_FAIL.getCode());
        // 同步返回结果
        data.setSyncResult(body);
        projectManageMapper.updateById(data);

        return CommonConstant.OK.equals(code);
    }
}